package top.lethelh.jianoa.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateRange;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.stereotype.Service;
import top.lethelh.jianoa.config.SystemConstants;
import top.lethelh.jianoa.db.dao.*;
import top.lethelh.jianoa.db.pojo.TbCheckin;
import top.lethelh.jianoa.db.pojo.TbFaceModel;
import top.lethelh.jianoa.db.pojo.TbHolidays;
import top.lethelh.jianoa.exception.JoaException;
import top.lethelh.jianoa.service.CheckinService;
import top.lethelh.jianoa.task.EmailTask;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;

/**
 * @author lh
 * @date 2022/2/19 2:11 下午
 * @desc
 */
@Service
@Scope("prototype")
@Slf4j
public class CheckinServiceImpl implements CheckinService {

    @Autowired
    private SystemConstants constants;

    @Autowired
    private TbHolidaysMapper holidaysMapper;

    @Autowired
    private TbWorkdayMapper workdayMapper;

    @Autowired
    private TbCheckinMapper checkinMapper;

    @Autowired
    private TbFaceModelMapper faceModelMapper;

    @Autowired
    private TbCityMapper cityMapper;

    @Autowired
    private TbUserMapper userMapper;

    @Autowired
    private EmailTask emailTask;

    @Value("${jianoa.face.createFaceModelUrl}")
    private String createFaceModelUrl;

    @Value("${jianoa.face.checkinUrl}")
    private String checkinUrl;

    @Value("${jianoa.code}")
    private String code;

    @Value("${jianoa.email.hr}")
    private String hrEmail;

    @Override
    public String validCanCheckIn(int userId, String date) {
        boolean isHoliday = holidaysMapper.searchTodayIsHolidays() != null;
        boolean isWorkday = workdayMapper.searchTodayIsWorkday() != null;
        String type = "工作日";
        if (DateUtil.date().isWeekend()) {
            type = "节假日";
        }
        if (isHoliday) {
            type = "节假日";
        } else if (isWorkday) {
            type = "工作日";
        }

        if (type.equals("节假日")) {
            return "节假日不需要考勤";
        } else {
            DateTime now = DateUtil.date();
            String start = DateUtil.today() + " " + constants.attendanceStartTime;
            String end = DateUtil.today() + " " + constants.attendanceEndTime;
            DateTime attendanceStart = DateUtil.parse(start);
            DateTime attendanceEnd = DateUtil.parse(end);
            if (now.isBefore(attendanceStart)) {
                return "没到上班考勤开始时间";
            } else if (now.isAfter(attendanceEnd)) {
                return "上班考勤已结束";
            } else {
                HashMap map = new HashMap();
                map.put("userId", userId);
                map.put("date", date);
                map.put("start", start);
                map.put("end", end);
                boolean bool = checkinMapper.haveCheckin(map) != null;
                return bool ? "今日已经考勤，不用重复考勤" : "可以考勤";
            }
        }
    }

    @Override
    public void checkin(HashMap map) {
        // 存储当前签到时间
        Date now = DateUtil.date();
        // 从系统中获取今日上班考勤开始时间
        Date todayAttendanceTime = DateUtil.parse(DateUtil.today() + " " + constants.attendanceTime);
        // 从系统中获取进入上班考勤结束的时间
        Date todayAttendanceEndTime = DateUtil.parse(DateUtil.today() + " " + constants.attendanceEndTime);
        //TODO 1 正常签到，2 签到异常 业务逻辑待完善
        int status = 1;
        if (now.compareTo(todayAttendanceTime) <= 0) {
            status = 1;
        } else if (now.compareTo(todayAttendanceTime) > 0 && now.compareTo(todayAttendanceEndTime) < 0) {
            status = 2;
        }

        // 从传入的map对象获得用户id
        int userId = (Integer)map.get("userId");
        // 之后根据userId从MySQL数据库查询人脸模型数据
        String faceModelInDB = faceModelMapper.searchFaceModel(userId);
        // 判断人脸模型是否存在
        if (faceModelInDB == null) {
            // 若不存在，则抛出业务错误
            throw new JoaException("No face model in DB!");
        } else {
            // 若存在，首先从map参数中获得 上传的人脸图片的临时路径
            String facePath = (String) map.get("path");
            // 使用Java爬虫，创建HttpRequest，准备发送http请求
            HttpRequest req = HttpUtil.createPost(checkinUrl);
            // 人脸识别服务器请求实际上是一个form表单，所以这边需要使用post请求，提交表单信息
            // 设置photo参数，上传临时人脸图片以及DB里的人脸模型数据
            req.form("photo", FileUtil.file(facePath), "targetModel", faceModelInDB);
            req.form("code", code);
            // 执行post请求，获得响应
            HttpResponse resp = req.execute();
            // 若从人脸识别服务器返回的响应非200
            if (resp.getStatus() != 200) {
                // 输出错误日志
                log.error("Face model service error!");
                // 抛出一个业务异常
                throw new JoaException("Face model service error!");
            }
            // 若返回正常，接收返回的请求体
            String respBody = resp.body();
            // 进行情况判断
            if ("无法识别出人脸".equals(respBody) || "照片中存在多张人脸".equals(respBody)) {
                // 符合本条件，则抛出业务异常
                throw new JoaException(respBody);
                // 如果返回为False，说明人脸识别不通过
            } else if ("False".equals(respBody)) {
                // 抛出业务异常
                throw new JoaException("签到失败，非本人签到");
                // 如果返回为True，说明人脸识别通过
            } else if ("True".equals(respBody)) {
                // 初始风险等级，默认为1（低风险）
                int level = 1;
                // 从map参数中获得签到城市
                String ciCity = (String) map.get("city");
                // 从map参数中获得签到区县
                String ciDistrict = (String) map.get("district");
                // 从map参数中获得签到地址
                String ciAddr = (String) map.get("address");
                // 从map参数中获得签到国家
                String ciCountry = (String) map.get("country");
                // 从map参数中获得签到省份
                String ciProvince = (String) map.get("province");
                // 使用Hutool进行非空验证
                if (!StrUtil.isBlank(ciCity) && !StrUtil.isBlank(ciDistrict)) {
                    // 从DB获取城市代码
                    String cityCode = cityMapper.searchCode(ciCity);
                    try {
                        // 从某权威网站获取疫情数据
                        String getUrl = "http://m." + cityCode + ".bendibao.com/news/yqdengji/?qu=" + ciDistrict;
                        // 使用Jsoup来解析html，然后使用文档类型来接收解析的html数据
                        Document htmlDoc = Jsoup.connect(getUrl).get();
                        // 抓取list-content这个节点
                        Elements elementCons = htmlDoc.getElementsByClass("list-content");
                        // 防御性编程
                        if (elementCons.size() > 0) {
                            // 获取第一个元素对象
                            Element first = elementCons.get(0);
                            // 从第一个元素抓取p标签，得到风险等级
                            String risk = first.select("p:last-child").text();
                            // 如果为高风险
                            if ("高风险".equals(risk)) {
                                // 设置风险等级为3 （高风险）
                                level = 3;
                                //TODO 发送告警邮件
                                // 首先获取用户个人信息
                                HashMap<String,String> info = userMapper.searchNameAndDept(userId);
                                // 获得用户名
                                String userName = info.get("name");
                                // 获得用户部门
                                String userDept = info.get("dept_name");
                                // 判定部门名是否存在
                                userDept = userDept != null ? userDept : "";

                                // 创建Java邮件对象
                                SimpleMailMessage smMessage = new SimpleMailMessage();
                                // 设置邮件接者
                                smMessage.setTo(hrEmail);
                                // 设置摘要信息
                                smMessage.setSubject("员工" + userName + "身处高风险地区警告邮件");
                                // 设置邮件正文
                                smMessage.setText("Hi, " + userDept + "员工" + userName + "，" + DateUtil.format(new Date(), "yyyy年MM月dd日") + "处于" + ciAddr + "，属于新冠疫情高风险地区，请及时与该员工联系，核实情况！");
                                // 使用邮件任务类发送异步邮件信息
                                emailTask.sendAsync(smMessage);
                                // 如为中风险地区
                            } else if ("中风险".equals(risk)) {
                                // 设置风险等级为2 （中风险）
                                level = 2;
                            }
                        }
                        // 上述代码如遇异常，捕获
                    } catch (Exception e) {
                        // 日志输出
                        log.error("Error!", e);
                        // 抛出业务异常
                        throw new JoaException("Get risk failed!");
                    }
                }

                TbCheckin entity=new TbCheckin();
                entity.setUserId(userId);
                entity.setAddress(ciAddr);
                entity.setCountry(ciCountry);
                entity.setProvince(ciProvince);
                entity.setCity(ciCity);
                entity.setDistrict(ciDistrict);
                entity.setStatus((byte) status);
                entity.setRisk(level);
                entity.setDate(DateUtil.today());
                entity.setCreateTime(now);
                checkinMapper.insert(entity);
            }
        }
    }

    @Override
    public void createFaceModel(int userId, String path) {
        HttpRequest request = HttpUtil.createPost(createFaceModelUrl);
        request.form("photo", FileUtil.file(path));
        request.form("code", code);
        HttpResponse response = request.execute();
        String body = response.body();
        if ("无法识别出人脸".equals(body) || "照片中存在多张人脸".equals(body)) {
            throw new JoaException(body);
        } else {
            TbFaceModel entity = new TbFaceModel();
            entity.setUserId(userId);
            entity.setFaceModel(body);
            faceModelMapper.insert(entity);
        }
    }

    @Override
    public HashMap searchTodayCheckin(int userId) {
        HashMap map = checkinMapper.searchTodayCheckin(userId);
        return map;
    }

    @Override
    public long searchCheckinDays(int userId) {
        long days = checkinMapper.searchCheckinDays(userId);
        return days;
    }

    @Override
    public ArrayList<HashMap> searchWeekCheckin(HashMap param) {
        ArrayList<HashMap> checkinList = checkinMapper.searchWeekCheckin(param);
        ArrayList<String> holidaysList = holidaysMapper.searchHolidaysInRange(param);
        ArrayList<String> workdayList = workdayMapper.searchWorkdayInRange(param);

        DateTime startDate = DateUtil.parseDate(param.get("startDate").toString());
        DateTime endDate = DateUtil.parseDate(param.get("endDate").toString());
        DateRange range = DateUtil.range(startDate, endDate, DateField.DAY_OF_MONTH);
        ArrayList list = new ArrayList();
        range.forEach(one -> {
            String date = one.toString("yyyy-MM-dd");
            //查看今天是不是假期或者工作日
            String type = "工作日";
            if (one.isWeekend()) {
                type = "节假日";
            }
            if (holidaysList != null && holidaysList.contains(date)) {
                type = "节假日";
            } else if (workdayList != null && workdayList.contains(date)) {
                type = "工作日";
            }

            String status = "";
            if (type.equals("工作日") && DateUtil.compare(one, DateUtil.date()) <= 0) {
                status = "缺勤";
                boolean flag=false;
                for (HashMap<String, String> map : checkinList) {
                    if (map.containsValue(date)) {
                        status = map.get("status");
                        flag=true;
                        break;
                    }
                }
                DateTime endTime=DateUtil.parse(DateUtil.today()+" "+constants.attendanceEndTime);
                String today=DateUtil.today();
                if(date.equals(today)&&DateUtil.date().isBefore(endTime)&&flag==false){
                    status="";
                }
            }

            HashMap map = new HashMap();
            map.put("date", date);
            map.put("status", status);
            map.put("type", type);
            map.put("day", one.dayOfWeekEnum().toChinese("周"));
            list.add(map);
        });
        return list;
    }

    @Override
    public ArrayList<HashMap> searchMonthCheckin(HashMap param) {
        return this.searchWeekCheckin(param);
    }
}
